/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>


/* crc_modbus[udp_server] */
#define UDP_SERVER_PORT 838

static const char *TAG = __FILE_NAME__;

char recv_data[1024];

static void udp_server_task(void *pvParameters)
{
    struct sockaddr_in server_addr = {0}, client_addr = {0};
    socklen_t client_sock_len;
    int server_sock, recv_len, err;

    ESP_LOGE(TAG, "udp_server_task");

    server_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (server_sock < 0)
    {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        goto __exit;
    }
    ESP_LOGI(TAG, "Socket created");

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(UDP_SERVER_PORT);

    err = bind(server_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    if (err < 0)
    {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        goto __exit;
    }
    ESP_LOGI(TAG, "Socket bound (%s: %d)\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));

    while (1)
    {
        memset(recv_data, 0, sizeof(recv_data));

        recv_len = recvfrom(server_sock,
                            recv_data,
                            sizeof(recv_data),
                            0,
                            (struct sockaddr*)&client_addr,
                            &client_sock_len);

        ESP_LOGI(TAG, "receive from (%s: %d), size: %d bytes: %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), recv_len, recv_data);

        if(recv_len)
        {
            err = sendto(server_sock, recv_data, recv_len, 0, (struct sockaddr *)&client_addr, sizeof(client_addr));
            if (err < 0)
            {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
        }
    }

__exit:
    ESP_LOGE(TAG, "Shutting down socket");
    shutdown(server_sock, 0);
    close(server_sock);

    vTaskDelete(NULL);
}

void udp_server_init(void)
{
    xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET, 5, NULL);
}
